var websockets = require("websockets");
var colors = require("colors");
var _snake = require("./snake.js");

//SERVER CODE 
function GameStateManager(clientManager) {
	this.clientManager = clientManager;
	this.snakeBoard = new _snake.SnakeBoard(60,40);
	this.snakes = [];
	this.food = undefined;
	this.players = {
		"player1": {joined: false, ready:false, colour:"red", loses: 0},
		"player2": {joined: false, ready:false, colour:"blue", loses: 0},
		"player3": {joined: false, ready:false, colour:"green", loses: 0}
	};
	this.gameInProgress = false;
	this.tickDelay = 150;
	this.tickCount = 0;
	this.server_msg = "Waiting for players...";
}

GameStateManager.prototype.createFood = function () {
	var width = this.snakeBoard.width;
	var height =this.snakeBoard.height;
	var x = 0;
	var y = 0;
	var validFoodPos = false;
	while (!validFoodPos) {
		x = Math.floor(Math.random()*width);
		y = Math.floor(Math.random()*height);
		console.log("Testing food pos:",x,y);
		if (this.snakeBoard.cells[x][y].occupant){
		} else {
			validFoodPos = true;
		}
	}
	this.food = new _snake.Food(x,y,this.snakeBoard);
	console.log('created food');
	this.clientManager.sendToAll(this.boardStatePacket());
};

GameStateManager.prototype.changeSnakeDirection = function (name, direction) {
	//find snake
	for(var s=0; s<this.snakes.length; s++) {
		if (this.snakes[s].name == name) {
			//update server snake
			this.snakes[s].changeDirection( direction );
			
			//update clients' snake
			this.clientManager.sendToAll({message:"direction", name:name, direction: direction});
		}
	}
	
};

GameStateManager.prototype.sendServerMessage = function () {
	this.clientManager.sendToAll({message: "server_msg", server_msg: this.server_msg});
}

GameStateManager.prototype.updateServerMessage = function (message) {
	this.server_msg = message;
	this.sendServerMessage();
}

GameStateManager.prototype.joinedPlayers = function () {
	var count = 0;
	for(var i in this.players){
		if (this.players[i].joined) {
			count++;
		}
	}
	return count;
};

GameStateManager.prototype.readyPlayers = function () {
	var count = 0;
	for(var i in this.players){
		if (this.players[i].ready) {
			count++;
		}
	}
	return count;
};

GameStateManager.prototype.startGame = function (){
	console.log("all players ready, starting game");
	this.updateServerMessage("");
	this.snakes = [];
	this.snakeBoard.clear();
	var clientManager = this;
	//Spawn players
	var x = 5;
	for(var player in this.players) {
		if (this.players[player].ready) {
			console.log("spawning "+player);
			var segments = _snake.Snake.generateSnakeSegments(x, 10, 5);
			console.log("created segments");
			var snake = new _snake.Snake(player,
								this.snakeBoard, 
								this.players[player].colour, 
								segments, 
								_snake.Snake.directions.UP,
								function () {clientManager.createFood()},
								function (name) {clientManager.gameOver(name)});
			console.log("created "+player);
			this.snakes.push(snake);
		}
		x+=5;
	}
	
	this.food = new _snake.Food(9,13, this.snakeBoard);
	
	//Send gamestate to all connected clients
	this.clientManager.sendToAll(this.boardStatePacket());
		
	//Start ticker
	this.tickCount = 0;
	this.gameInProgress = true;
	var gameStateManager = this;
	setTimeout(function(){gameStateManager.countDown(gameStateManager, 3);}, 100);
};

GameStateManager.prototype.countDown = function (gameStateManager, number) {
	//Check if everyone is ready
	if (this.joinedPlayers() == this.readyPlayers()) {
		if (number > 0) {
			gameStateManager.updateServerMessage("Game starting in " + number);
			setTimeout(function () {
				gameStateManager.countDown(gameStateManager, number - 1);
			}, 1000);
		} else {
			gameStateManager.updateServerMessage("");
			setTimeout(function () {
				gameStateManager.tick(gameStateManager);
			}, gameStateManager.tickDelay);
		}
	//else stop the countdown and fallback to lobby
	} else {
		this.gameOver();
	}
}

GameStateManager.prototype.gameOver = function (loserName) {
	this.gameInProgress = false;
	if (loserName) {
		console.log(loserName+" lost the game");
		this.players[loserName].loses += 1;
		this.clientManager.sendToAll({message: "gameOver", loser: loserName});
		this.updateServerMessage(loserName+" lost!");
	} else {
		console.log("last player left the game");
		this.clientManager.sendToAll({message: "gameOver"});
		this.updateServerMessage("Waiting for players...");
	}

	//Set all players as not ready
	for(var i in this.players){
		this.players[i].ready = false;
	}
	
	this.clientManager.sendToAll(this.playerStatePacket());
};

GameStateManager.prototype.newConnection = function () {
	this.clientManager.sendToAll(this.playerStatePacket());
	this.sendServerMessage();
	if (this.gameInProgress) {
		this.clientManager.sendToAll(this.boardStatePacket());

	}
};

GameStateManager.prototype.tick = function (gameStateManager){
	for(var i=0; i<this.snakes.length; i++) {
		this.snakes[i].tick();
	}
	if (gameStateManager.gameInProgress) {
		gameStateManager.clientManager.sendToAll({message:"tick"});
		setTimeout(function(){gameStateManager.tick(gameStateManager);}, gameStateManager.tickDelay);  
	}
	this.tickCount += 1;
};

GameStateManager.prototype.addPlayer = function (name) {
	if (this.players[name]) {
		console.log("adding player "+name)
		this.players[name].joined = true;
		this.clientManager.sendToAll(this.playerStatePacket());
	} else {
		console.log("added player that is not registered".red);
	}
};

GameStateManager.prototype.removePlayer = function (name) {
	if (this.players[name]) {
		console.log("remove player "+name)
		this.players[name].joined = false;
		this.players[name].ready = false;
		this.clientManager.sendToAll(this.playerStatePacket());	
		if (this.readyPlayers() == 0){
			this.gameOver();
		}	
	} else {
		console.log("removed player that is not registered".red);
	}
};

GameStateManager.prototype.readyPlayer = function (name, ready) {
	if (this.gameInProgress && ready) {
		return;
	}
	if (this.players[name]) {
		console.log("ready player "+name+" "+ready)
		this.players[name].ready = ready;
		this.clientManager.sendToAll(this.playerStatePacket());
		if (this.readyPlayers() == 0){
			this.gameOver();
		}	
	} else {
		console.log("ready'ed player that is not registered".red);
	}
	
	console.log(this.joinedPlayers(), this.readyPlayers());
	//Start the game if all players are ready
	if (this.joinedPlayers() == this.readyPlayers()) {
		this.startGame();
	}
};

GameStateManager.prototype.playerStatePacket = function () {
	console.log(this.players);
	return {message: "playerState", players: this.players};
};

GameStateManager.prototype.boardStatePacket = function () {
	var snakes = [];
	
	for (var i=0; i<this.snakes.length; i++) {
		snakes.push({
			name: this.snakes[i].name,
			colour: this.snakes[i].colour,
			direction: this.snakes[i].direction,
			segments: this.snakes[i].segments
		});
	}
	
	//return console.log(JSON.stringify({message: "boardState", boardState: {snakes: snakes, food: {x: this.food.x, y: this.food.y}}}));
	return {message: "boardState", boardState: {snakes: snakes, food: {x: this.food.x, y: this.food.y}}};
};

    
var authentication = {
	player1: "player1",
	player2: "player2",
	player3: "player3"
};


function Client ( socket ) {
	this.username = "someone";
	this.socket = socket;
	this.socket.client = this;
	this.socket.on('message', this.receive); 
	this.socket.on('close', this.socketCloseHandler); 
	
}

Client.prototype.socketCloseHandler = function() {
	this.client.removeClient();
};

Client.prototype.close = function () {
	this.socket.close();
	this.removeClient();
};

Client.prototype.removeClient = function() {
	var index = clientManager.clients.indexOf(this);
	console.log("Removing client from manager".yellow);
	gameStateManager.removePlayer(this.username);
	if (index > -1) {
		clientManager.clients.splice(index, 1);
	}
};

Client.prototype.receive = function( message ) {
	try {
		data = JSON.parse(message);
		if (data.username && authentication[data.username] == data.password) {
			console.log("authed message");
			if (data.message == "auth") {
				this.client.sendObject({message: "auth",auth: true});
				gameStateManager.addPlayer(data.username);
				this.client.username = data.username;
			
			} else if (data.message == "move") { 
				console.log(data.username, data.direction);
				gameStateManager.changeSnakeDirection(data.username, data.direction);
			
			}else if (data.message == "ready"){
				gameStateManager.readyPlayer(data.username, data.ready);
			} else {
				
			}
        } else {
			this.client.sendObject({message: "auth",auth: false});
		}
	} catch (e){
		console.log("invalid message".red,e)
	}
};

Client.prototype.sendObject = function ( object ) {
	this.socket.send(JSON.stringify(object))
};

function ClientManager () {
	this.clients = [];
}

ClientManager.prototype.addClient = function ( client ) {
	this.clients.push( client );
};

ClientManager.prototype.sendToAll = function ( object ) {
	for(var i=0; i<this.clients.length;i++){
		this.clients[i].sendObject(object);
	}
};


var server = websockets.createServer();

var clientManager = new ClientManager();

var gameStateManager = new GameStateManager(clientManager);

server.on('error', function ( error ) { console.log(error); } );

server.on('connect', function (socket) {
	console.log('New Connection'.yellow);//, clientManager.clients.length, valid)
	clientManager.addClient( new Client(socket) );
	gameStateManager.newConnection();  //sends server state to all clients
}).listen(81);

console.log( 'Server Running!');
